import json

from django.shortcuts import render
from django.http import HttpResponse

from elasticsearch import Elasticsearch

import nltk
import nltk.corpus
from nltk.stem import SnowballStemmer

def recommend(text, ontology):
    text = process_text(text)

    tag_list = recommend_text(ontology, text)
    recommend_result = format_recommend_result(tag_list)
    return recommend_result

def search(text, ontology):
    tag_list = search_text(ontology, text)
    search_result = format_search_result(tag_list)
    return search_result

def go(text, ontology):
    tag_list = go_text(ontology, text)
    go_result = format_go_result(tag_list)
    return go_result

def process_text(text):
    original_word_list = nltk.word_tokenize(text)
    stem_list = []
    stemmer = SnowballStemmer("english")
    stopwords = set(nltk.corpus.stopwords.words('english'))

    for word in original_word_list:
        if not word in stopwords:
            stem_list.append(stemmer.stem(word))

    processed_text = " ".join(stem_list)

    return processed_text

def recommend_text(ontology, text):
    es = Elasticsearch()
    tag_list = []
    found_tags = set()
    tag_list.extend(match_search_text(es, text, found_tags))
    tag_list.extend(fuzzy_search_text(es, text, found_tags))
    tag_list = filter_by_ontology(tag_list, ontology)

    return tag_list

def search_text(ontology, text):
    es = Elasticsearch()
    tag_list = []
    found_tags = set()
    tag_list.extend(match_search_text(es, text, found_tags))
    tag_list = filter_by_ontology(tag_list, ontology)

    return tag_list

def go_text(ontology, text):
    es = Elasticsearch()
    tag_list = []
    found_tags = set()
    tag_list.extend(match_search_text(es, text, found_tags))
    tag_list = filter_by_ontology(tag_list, ontology)

    return tag_list

def match_search_text(es, text, found_tags):
    query_body = {
            "from": 0,
            "size": 30,
            "query": {
                "match": {
                    "name": text
                }
            }
    }
    res = es.search(index="mitagger", doc_type="tag", body=query_body)

    hits = res['hits']['hits']
    tag_list = []

    max_score = find_max_score(hits)
    for hit in hits:
        hit_source = hit['_source']
        if not hit_source['@id'] in found_tags:
            hit_source['_score'] = hit['_score'] / max_score * 0.4 + 0.6
            tag_list.append(hit['_source'])
            found_tags.add(hit_source['@id'])

    return tag_list

def fuzzy_search_text(es, text, found_tags):
    query_body = {
            "from": 0,
            "size": 30,
            "query": {
                "fuzzy_like_this": {
                    "fields": ["name"],
                    "like_text": text,
                    "max_query_terms": 30
                }
            }
    }
    res = es.search(index="mitagger", doc_type="tag", body=query_body)

    hits = res['hits']['hits']
    tag_list = []

    max_score = find_max_score(hits)
    for hit in hits:
        hit_source = hit['_source']
        if not hit_source['@id'] in found_tags:
            hit_source['_score'] = hit['_score'] / max_score * 0.3 + 0.3
            tag_list.append(hit['_source'])
            found_tags.add(hit_source['@id'])

    return tag_list

def find_max_score(hits):
    max_score = 0
    for hit in hits:
        score = hit['_score']
        if score > max_score:
            max_score = score

    return max_score

def filter_by_ontology(tag_list, ontology):
    if ontology == "all":
        return tag_list

    ontology = ontology.lower()
    ontology_list = ontology.split(',')
    filtered_tag_list = []
    for tag in tag_list:
        tag_ontology = tag['ontology'].lower()
        for ontology in ontology_list:
            if tag_ontology.endswith(ontology):
                filtered_tag_list.append(tag)

    return filtered_tag_list

# [ 
#   {
#       score: score,
#       annotatedClasses: [
#       {
#           @id: tagId,
#           links: {
#               ontology: ontology_link
#           }
#       },...
#       ]
#   },...
# ]
def format_recommend_result(tag_list):
    recommend_map = {}
    for tag in tag_list:
        tag_group_by_score = None
        score = tag['_score']

        if recommend_map.has_key(score):
            tag_group_by_score = recommend_map[score]
        else:
            tag_group_by_score = {
                    "score": score,
                    "annotatedClasses": []
            }
            recommend_map[score] = tag_group_by_score

        tag_group_by_score['annotatedClasses'].append({
            '@id': tag['@id'],
            'links': {
                'ontology': tag['ontology']
            }
        })

    recommend_list = []
    for score in recommend_map:
        recommend_list.append(recommend_map[score])
    recommend_list = sorted(
            recommend_list, 
            key=lambda recommend : recommend['score'],
            reverse=True
    )

    return recommend_list

# [ {
#   annotatedClass: {
#       @id: tagId,
#       links: {
#           ontology: ontology_link
#       },
#   },
#   hierarchy: [],
#   annotations: [ {
#       from: to: matchType: text
#   }]
#   }
# ]
def format_search_result(tag_list):
    result = []
    i = 0
    for tag in tag_list:
        annotatedClass = {
                "@id": tag["@id"],
                "links": {
                    "ontology": tag["ontology"]
                }
        }
        hierarchy = []
        annotations = [{
            "from": 1,
            "to": 8,
            "matchType": "PREF" if i == 0 else "SYN"
        }]

        result.append({
            "annotatedClass": annotatedClass,
            "hierarchy": hierarchy,
            "annotations": annotations
        })

        i = i + 1

    return result

# [ {
#   annotatedClass: {
#       @id: tagId,
#       links: {
#           ontology: ontology_link
#       },
#   },
#   annotations: [ {
#       from: to: matchType: text
#   }]
#   }
# ]
def format_go_result(tag_list):
    result = []
    i = 0
    for tag in tag_list:
        annotatedClass = {
                "@id": tag["@id"],
                "links": {
                    "ontology": tag["ontology"]
                }
        }
        annotations = [{
            "from": 1,
            "to": 8,
            "matchType": "PREF" if i == 0 else "SYN"
        }]

        result.append({
            "annotatedClass": annotatedClass,
            "annotations": annotations
        })

        i = i + 1

    return result
